package com.childenglish.utils;

import org.apache.tika.Tika;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.List;
import java.util.regex.Pattern;

/**
 * 安全工具类
 * 提供数据加密、文件安全检查等功能
 */
public class SecurityUtils {

    private static final Logger logger = LoggerFactory.getLogger(SecurityUtils.class);
    private static final Tika tika = new Tika();

    // 危险文件扩展名
    private static final List<String> DANGEROUS_EXTENSIONS = Arrays.asList(
        "exe", "bat", "cmd", "com", "pif", "scr", "vbs", "js", "jar", "sh",
        "php", "jsp", "asp", "aspx", "py", "rb", "pl", "cgi"
    );

    // 危险文件MIME类型
    private static final List<String> DANGEROUS_MIME_TYPES = Arrays.asList(
        "application/x-msdownload",
        "application/x-executable",
        "application/x-msdos-program",
        "application/x-ms-installer",
        "application/x-sh",
        "application/x-shellscript"
    );

    // 恶意代码模式
    private static final Pattern[] MALICIOUS_PATTERNS = {
        Pattern.compile("<\\?php", Pattern.CASE_INSENSITIVE),
        Pattern.compile("<%", Pattern.CASE_INSENSITIVE),
        Pattern.compile("eval\\s*\\(", Pattern.CASE_INSENSITIVE),
        Pattern.compile("exec\\s*\\(", Pattern.CASE_INSENSITIVE),
        Pattern.compile("system\\s*\\(", Pattern.CASE_INSENSITIVE),
        Pattern.compile("shell_exec", Pattern.CASE_INSENSITIVE),
        Pattern.compile("passthru", Pattern.CASE_INSENSITIVE),
        Pattern.compile("proc_open", Pattern.CASE_INSENSITIVE),
        Pattern.compile("popen", Pattern.CASE_INSENSITIVE),
        Pattern.compile("base64_decode", Pattern.CASE_INSENSITIVE),
        Pattern.compile("gzinflate", Pattern.CASE_INSENSITIVE),
        Pattern.compile("str_rot13", Pattern.CASE_INSENSITIVE)
    };

    /**
     * 检测文件是否包含恶意代码
     * 扫描文件内容中的危险模式
     */
    public static boolean containsMaliciousCode(MultipartFile file) {
        try {
            // 只检查文本文件
            String contentType = file.getContentType();
            if (contentType == null || !contentType.startsWith("text/")) {
                return false;
            }

            // 读取文件内容（限制大小，避免内存溢出）
            byte[] buffer = new byte[Math.min((int) file.getSize(), 10240)]; // 最多读取10KB
            try (InputStream is = file.getInputStream()) {
                int bytesRead = is.read(buffer);
                if (bytesRead > 0) {
                    String content = new String(buffer, 0, bytesRead, StandardCharsets.UTF_8);
                    
                    // 检查恶意代码模式
                    for (Pattern pattern : MALICIOUS_PATTERNS) {
                        if (pattern.matcher(content).find()) {
                            logger.warn("检测到恶意代码模式: {} 在文件: {}", 
                                pattern.pattern(), file.getOriginalFilename());
                            return true;
                        }
                    }
                }
            }
        } catch (IOException e) {
            logger.error("扫描文件内容时出错: {}", file.getOriginalFilename(), e);
            // 如果无法读取文件，为了安全起见，拒绝上传
            return true;
        }
        return false;
    }

    /**
     * 使用Tika检测真实文件类型
     * 防止通过修改扩展名绕过检查
     */
    public static String detectRealFileType(MultipartFile file) {
        try {
            String detectedType = tika.detect(file.getInputStream(), file.getOriginalFilename());
            logger.debug("文件 {} 的真实类型: {}", file.getOriginalFilename(), detectedType);
            return detectedType;
        } catch (IOException e) {
            logger.error("检测文件类型失败: {}", file.getOriginalFilename(), e);
            return null;
        }
    }

    /**
     * 验证文件类型是否安全
     */
    public static boolean isSafeFileType(MultipartFile file) {
        // 检查扩展名
        String originalFilename = file.getOriginalFilename();
        if (originalFilename != null) {
            String extension = getFileExtension(originalFilename).toLowerCase();
            if (DANGEROUS_EXTENSIONS.contains(extension)) {
                logger.warn("危险文件扩展名: {} 在文件: {}", extension, originalFilename);
                return false;
            }
        }

        // 检查MIME类型
        String contentType = file.getContentType();
        if (contentType != null && DANGEROUS_MIME_TYPES.contains(contentType)) {
            logger.warn("危险MIME类型: {} 在文件: {}", contentType, originalFilename);
            return false;
        }

        // 使用Tika检测真实文件类型
        String realType = detectRealFileType(file);
        if (realType != null && DANGEROUS_MIME_TYPES.contains(realType)) {
            logger.warn("检测到危险的真实文件类型: {} 在文件: {}", realType, originalFilename);
            return false;
        }

        return true;
    }

    /**
     * 综合文件安全检查
     */
    public static FileSecurityCheckResult checkFileSecurity(MultipartFile file) {
        FileSecurityCheckResult result = new FileSecurityCheckResult();
        result.setFileName(file.getOriginalFilename());

        // 1. 检查文件类型
        if (!isSafeFileType(file)) {
            result.setSafe(false);
            result.addIssue("文件类型不安全");
            return result;
        }

        // 2. 检查恶意代码
        if (containsMaliciousCode(file)) {
            result.setSafe(false);
            result.addIssue("文件包含恶意代码");
            return result;
        }

        // 3. 检查文件大小
        if (file.getSize() > 10 * 1024 * 1024) { // 10MB
            result.setSafe(false);
            result.addIssue("文件大小超过限制");
            return result;
        }

        result.setSafe(true);
        return result;
    }

    /**
     * 获取文件扩展名
     */
    private static String getFileExtension(String fileName) {
        if (fileName == null || fileName.lastIndexOf(".") == -1) {
            return "";
        }
        return fileName.substring(fileName.lastIndexOf(".") + 1);
    }

    /**
     * 文件安全检查结果
     */
    public static class FileSecurityCheckResult {
        private String fileName;
        private boolean safe = true;
        private List<String> issues = new java.util.ArrayList<>();

        public String getFileName() {
            return fileName;
        }

        public void setFileName(String fileName) {
            this.fileName = fileName;
        }

        public boolean isSafe() {
            return safe;
        }

        public void setSafe(boolean safe) {
            this.safe = safe;
        }

        public List<String> getIssues() {
            return issues;
        }

        public void addIssue(String issue) {
            this.issues.add(issue);
        }

        public String getMessage() {
            if (issues.isEmpty()) {
                return "文件安全检查通过";
            }
            return String.join(", ", issues);
        }
    }
}

